<template>
  <div :id="props.mapId + '_Parent'" style="width: 0px; height: 0px">
    <div
      :id="props.mapId"
      style="width: 100%; height: 100%; background-color: rgb(213 223 223)"
    ></div>
    <div
      v-if="isMapImageLoaded"
      :id="props.mapId + '_Placeholder'"
      style="
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      "
      :style="{}"
    >
      <a-result style="text-align: center" :status="resultMap.status" :sub-title="resultMap.title">
        <template #icon> <question-circle-filled></question-circle-filled> </template
        ><template #extra>
          <a-button type="primary">Retry</a-button>
        </template>
      </a-result>
    </div>
  </div>
</template>
<!-- 通常来说map会铺满父节点的空间，所以外层vue一般如下嵌入FhcLeaflet 
<div style="flex: 1;  margin-bottom: 15px">
      <FhcLeaflet
        :map-id="'homeMapId'"
        :max-zoom="3"
        :min-zoom="-3"
        @on-map-created="onMapCreatedImpl"
      >
      </FhcLeaflet>
    </div>
-->
<script setup lang="ts">
import { ref, nextTick, watch, onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
import {
  xy,
  initMap,
  onResetMapZoomRange,
  showFullHeatmap,
  refreshFullHeatmapData
} from './FhcLeaflet'
import L from 'leaflet'
import { QuestionCircleFilled } from '@ant-design/icons-vue'
import { HeatLeafletMap } from './leaflet-heatmap'
import { watchThrottled, useDebounceFn, useThrottleFn } from '@vueuse/core'

import { type StoreData } from '../../Heatmap/index'
import { genBitmapImage } from './LeafletUtil'

let map: L.Map | null
let mapCreateTimer: number | null
let resizeObserverBody: ResizeObserver

let options: L.MapOptions
const resultMap = ref({ status: 'none', title: null })

const props = defineProps<{
  mapId: string
  maxBounds?: L.LatLng[] | undefined
  fullScreen?: boolean
  isMapImageLoaded?: boolean
  imageObj: { src: string; height: number; width: number } | null
  heatmapData?: StoreData
  isFullHeatmap?: boolean
}>()

watch(
  () => props.imageObj,
  () => {
    if (map && props.imageObj) {
      map.imageSrcString = props.imageObj.src
      map.imageMapHeight = props.imageObj.height
      map.imageMapWidth = props.imageObj.width
      onResetMapImage()
    }
  },
  { deep: true }
)
watchThrottled(
  () => props.heatmapData,
  () => {
    if (props.isFullHeatmap == null) {
      return
    } else {
      //数据量大的时候》3000，实时更新的话还是会有卡顿，可以考虑dragstart,zoomstart时停止更新，
      nextTick(() => {
        reloadHeatmapData()
      })
    }
  },
  { throttle: 2000, deep: true }
)

const reloadHeatmapData = useThrottleFn(() => {
  console.log('throttledFn resetHeatmapData >>>>>>', new Date(), props.isFullHeatmap)
  if (!map || !imageOverlay || props.isFullHeatmap == null) return
  if (props.isFullHeatmap) {
    if (map.heatmap) {
      refreshFullHeatmapData(map, props.heatmapData)
    } else {
      fullHeatmapDiv = showFullHeatmap(map, props.heatmapData)
    }
  } else {
    if (heatmapLeaf) {
      heatmapLeaf.setData(props.heatmapData)
    } else {
      heatmapLeaf = new HeatLeafletMap(map, imageOverlay)
    }
  }
}, 1000)

watch(
  () => props.isFullHeatmap,
  (newVal, oldVal) => {
    console.log('watch isFullHeatmap', newVal, oldVal)
    redrawHeatMap()
  }
)

function redrawHeatMap() {
  clearHeatmap()
  if (map) {
    showHeatMap()
    if (props.isFullHeatmap) {
      //地图可视范围变化时 可以只监听movestart，moveend
      //那为何使用dragstart,dragend,zoomstart,zoomend去刷新或重置热力图？
      //原因在于dragstart,dragend可视范围虽变化，地图可视范围范围大小和imagovelay大小未变 热力图又不能跟随imagovelay移动，所以只需要设置可见不可见，
      //而 zoomstart,zoomend时可视范围大小和imagovelay大小都改变了，所以需要设置刷新
      map.on('zoomstart', onRefreshFullHeatmapData)
      map.on('zoomend', onRefreshFullHeatmapData)
      map.on('dragstart', onHideFullHeatmap)
      map.on('dragend', onDisplayFullHeatmap)
    }
  }
}

onMounted(() => {
  resizeObserverBody = new ResizeObserver(() => {
    debouncedFnbody()
  })

  resizeObserverBody.observe(document.body)

  window.addEventListener('resize', debouncedFnWindow)
  options = {
    crs: L.CRS.Simple,
    zoomControl: true,
    minZoom: -2,
    maxZoom: 2,
    fullscreenControl: props.fullScreen
  }
  createMap()
  onResizeMap('aaa')
})

onUnmounted(() => {
  console.log('FhcLeaflet>>  onUnmounted', props.mapId)
  clearHeatmap()
  if (map) {
    //
    map.off('overlayadd', overlayaddFn)
    map.off('overlayremove', overlayremoveFn)
    map.off('dragstart', onHideFullHeatmap)
    map.off('dragend', onDisplayFullHeatmap)
    map.off('zoomstart', onRefreshFullHeatmapData)
    map.off('zoomend', onRefreshFullHeatmapData)
    map.off()
    map.remove()
  }
  map = null
  if (resizeObserverBody) resizeObserverBody.disconnect()
})

onActivated(() => {
  createMap()
})

onDeactivated(() => {
  if (mapCreateTimer) {
    clearTimeout(mapCreateTimer)
  }
})

const LN_heat = 'heatmap'

let layerControl: L.Control.Layers | null = null
let layers_heat: L.LayerGroup | null = null
let fullHeatmapDiv: HTMLDivElement | null = null
// function resetHeatmap() {
//   if (map) {
//     if (props.heatmapData && props.heatmapData.data.length > 0) {
//       if (layerControl == null) {
//         layerControl = L.control.layers({}).addTo(map)
//         map.on('overlayadd', overlayaddFn)
//         map.on('overlayremove', overlayremoveFn)
//         layers_heat = L.layerGroup()
//         layerControl.addOverlay(layers_heat, LN_heat)
//       }
//       showHeatMap()
//     } else {
//       clearHeatmap()
//       map.off('overlayadd', overlayaddFn)
//       map.off('overlayremove', overlayremoveFn)
//       if (layers_heat) layerControl?.removeLayer(layers_heat)
//       if (layerControl) map.removeControl(layerControl)
//       layers_heat = null
//       layerControl = null
//     }
//   }
// }

//----------------------------------------------------------------
let imageOverlay: L.ImageOverlay | null
function onResetMapImage(): boolean {
  clearHeatmap()
  if (map) {
    console.log('onResetMapImage2222222222')
    map.eachLayer(function (layer) {
      map!.removeLayer(layer)
    })
    imageOverlay = null
    setTimeout(() => {
      if (map) {
        if (map.imageSrcString) {
          imageOverlay = L.imageOverlay(map.imageSrcString, [
            [0, 0],
            [map.imageMapHeight, map.imageMapWidth]
          ])
          imageOverlay.addTo(map)
          console.log('onResetMapImage111111111111111')
          onResetMapZoomRange(map, imageOverlay)
        }
      }
    }, 10)
  }
  return true
}

const emits = defineEmits<{
  (event: 'onMapCreated', map: L.Map): L.Map
  (event: 'onFullScreenChange', isFull: boolean): () => void
  (event: 'onMapResize', isFull: boolean): () => void
}>()

const debouncedFnbody = useDebounceFn(() => {
  onResizeMap('body')
  // 原本以为onRefreshFullHeatmapData 就可以fit浏览器，后来发现在浏览器最大化变为缩小后，
  // 和缩小变为最大化后('leaflet-pane leaflet-map-pane')的size会比较奇怪，不是fit mapDiv
  // 也就是map div resize后('leaflet-pane leaflet-map-pane')的size和map div不一致
  // 所以不得已redrawHeatMap
  redrawHeatMap()
}, 100)

const debouncedFnWindow = useDebounceFn(() => {
  onResizeMap('window')
  redrawHeatMap()
}, 100)

function createMap() {
  if (!map) {
    console.log('FhcLeaflet>>  createMap', props.mapId)
    if (options.crs === null || options.crs === undefined) delete (options as any).crs
    map = initMap(props.mapId, options)
    if (map) {
      emits('onMapCreated', map)

      if (props.imageObj) {
        onResetMapImage()
        if (props.isFullHeatmap) {
          map.on('zoomstart', onRefreshFullHeatmapData)
          map.on('zoomend', onRefreshFullHeatmapData)
          map.on('dragstart', onHideFullHeatmap)
          map.on('dragend', onDisplayFullHeatmap)
        }
      }
    }
  }

  mapCreateTimer = window.setTimeout(() => {}, 100)
}

function onResizeMap(from: string) {
  console.log('onResizeMap', from)
  let divApp = document.getElementById('app') as HTMLDivElement
  divApp.style.height = document.body.clientHeight - 2 + 'px'

  let divMapParent = document.getElementById(props.mapId + '_Parent') as HTMLDivElement
  if (!divMapParent) return
  let divMapParentParent = divMapParent.parentElement as HTMLElement
  let rect = divMapParentParent.getBoundingClientRect()

  //正常应该-15，对应外围margin-bottom: 15px，但是浏览器resize（宽< 高）后出现纵向滚动条，所以-25

  let offset = document.body.clientWidth < 1024 ? 30 : 10 //这个1024应该是css里对的min-width

  let h = document.body.clientHeight - rect?.y - 20
  let w = document.body.clientWidth - rect.x - offset
  divMapParentParent.style.height = h + 'px'
  divMapParentParent.style.width = w + 'px'

  divMapParent.style.height = divMapParentParent.style.height
  divMapParent.style.width = divMapParentParent.style.width

  onResetMapZoomRange(map!, imageOverlay)
  onRefreshFullHeatmapData()
}

const onRefreshFullHeatmapData = function () {
  console.log('onDisplayFullHeatmap  onRefreshFullHeatmapData')
  if (props.isFullHeatmap) {
    // 不加settimeout的话，在数据量大的时候拖动地图，map.off('moveend', onRefreshFullHeatmapData) 会有迟钝
    setTimeout(() => {
      onDisplayFullHeatmap()
      refreshFullHeatmapData(map!, props.heatmapData)
    }, 100)
  }
}

const overlayaddFn = function (e: L.LayersControlEvent) {
  console.log('overlayadd', e.name)
}

const overlayremoveFn = function (e: L.LayersControlEvent) {
  console.log('overlayremove', e.name)
}

let heatmapLeaf: HeatLeafletMap | null = null
const clearHeatmap = function () {
  if (map) {
    map.off('zoomstart', onHideFullHeatmap)
    map.off('zoomend', onRefreshFullHeatmapData)
    map.off('dragstart', onHideFullHeatmap)
    map.off('dragend', onDisplayFullHeatmap)
    map.heatmap = null
  }
  if (fullHeatmapDiv) {
    let p = fullHeatmapDiv.parentElement
    if (p) p.removeChild(fullHeatmapDiv)
    fullHeatmapDiv = null
  }
  if (heatmapLeaf) {
    heatmapLeaf.destory()
    heatmapLeaf = null
  }
}
const onHideFullHeatmap = function () {
  if (fullHeatmapDiv) {
    fullHeatmapDiv.style.display = 'none'
  }
}

const onDisplayFullHeatmap = function () {
  if (fullHeatmapDiv) {
    setTimeout(() => {
      fullHeatmapDiv.style.display = 'block'
    }, 100)
  }
}

/**
 * 显示热力图
 * 1.清除原有热力图，如果是full热力图
 */
function showHeatMap() {
  // clearHeatmap()
  if (!map || !imageOverlay || props.isFullHeatmap == null || props.heatmapData == null) return
  if (props.isFullHeatmap) {
    fullHeatmapDiv = showFullHeatmap(map, props.heatmapData)
  } else {
    if (!heatmapLeaf) heatmapLeaf = new HeatLeafletMap(map, imageOverlay)
    heatmapLeaf.setData(props.heatmapData)
  }
}
</script>
